/**
 *******************************************************************************
 * \brief   CAS EBX Project Work "Kaffeemaschine"
 *
 * \file    channel_main.c
 * \version 1.0
 * \date    30.06.2010
 * \author  Baehler Thomas, Liechti Beat, Sauter Marc
 * \author  Vuksic Valentina, Zbinden Rene
 * 
 * \details	module test for channels
 * \details build with: 
 * \details gcc channel.c channel_main.c -o channel_main -lrt
 * \details then run with (example):
 * \details channel_main 50
 *
 * \remark  Last Modifications:
 * \remark  V1.0, 30.06.2010   Initial release
 * \remark  V1.1, 01.07.2010   First working implementation of channel with fifo
 * \remark  V1.2, 01.07.2010   Added R & W semaphore, blocking write (bug) 
 *******************************************************************************
 **/

#include <termios.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// -- required for inet_aton when not using makefile???
//#define __USE_MISC

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <poll.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
//#include <sys/sendfile.h>
#include <sys/wait.h>
#include <time.h>
#include <sys/times.h>
#include <sched.h>

#include "channel.h"

#define RETCODE_SUCCESS		0
#define RETCODE_ERROR		-1

static int NonBlockingGetChar();

static int NonBlockingGetChar()
{
	struct termios oldt,
	newt;
	int ch;
	tcgetattr( STDIN_FILENO, &oldt );
	newt = oldt;
	/*! turn off canonical mode */
	newt.c_lflag &= ~( ICANON | ECHO );
	/*! minimum of number input read */
	newt.c_cc[VMIN] = 0;
	tcsetattr( STDIN_FILENO, TCSANOW, &newt );
	ch = getchar();
	/*! turn on canonical mode */
	tcsetattr( STDIN_FILENO, TCSANOW, &oldt );
	return ch;
}

void usage(char *progname)
{
	printf("usage:\n\t%s <# of children>\n", progname);
}

void runChild(int childNo)
{
	// -- get pid of child
	pid_t pid = getpid();
	printf("forked child %d, pid %ld\n", childNo, (long int)pid);
	
	srand((unsigned int)pid);
	
	// -- block all signals
	sigset_t old_sigmask;
	sigset_t wait_sigmask;
	sigset_t pen_sigmask;
	sigfillset(&wait_sigmask);
	sigprocmask(SIG_BLOCK, &wait_sigmask, &old_sigmask);
	
	// -- open channel for write
	tChannel *channelWrite = openChannelWrite("gugu01", sizeof(pid_t));
	if (channelWrite == NULL)
	{
		perror("openChannelWrite");
	}
	else
	{
		printf("openChannelWrite returned %d\n", channelWrite->queue);
		//for(;;usleep((childNo + 1) * 200))
		//for(;;sched_yield())
		for(;;)
		{
			// -- get pending signals
			if (sigpending(&pen_sigmask) < 0) 
			{
				perror("sigpending");
			}
			
			// -- check if SIGTERM is in queue
			int res = sigismember(&pen_sigmask, SIGTERM);
			if (res < 0)  
			{
				// -- error
				perror("sigismember");
			}
			else if (res > 0)
			{
				// -- break
				printf("child %lu got SIGTERM\n", (unsigned long)pid);
				break;
			}
			
			printf("writing %lu\n", (unsigned long)pid);
			int write = writeChannel(channelWrite, &pid);
			if (write != sizeof(pid))
			{
				perror("write to channel returned not expected value"); 
			}
			int sleep = rand() % pid;
			printf("sleeping %lu\n", (unsigned long)pid);
			usleep(sleep);
		}
		closeChannel(channelWrite);
	}
	exit(0);
}

void runParent(int noOfChannels)
{
	// -- parent is reader
	tChannel *channelRead = openChannelRead("gugu01", sizeof(pid_t));
	if (channelRead == NULL)
	{
		perror("openChannelRead");
	}
	else
	{
		printf("openChannelRead returned file descriptor %d\n", channelRead->queue);
		// -- fork children (writer)
		pid_t *pids = alloca(sizeof(pid_t) * noOfChannels);
		int childNo;
		for (childNo = 0; childNo < noOfChannels; childNo++)
		{
			pid_t pid = fork();
			if (pid == 0)
			{
				runChild(childNo);
			}
			else
			{
				pids[childNo] = pid;
				usleep(500);
			}
		}
		for(;NonBlockingGetChar() <= 0;)
		{
			pid_t pid;
			int read = readChannel(channelRead, &pid);
			if (read > 0)
			{
				printf("read value %d from channel\n", pid);
			}

			usleep(10);
		}
		for (childNo = 0; childNo < noOfChannels; childNo++)
		{
			kill(pids[childNo], SIGTERM);
			/* -- empty queue from pending messages */
			pid_t pid;
			while (waitpid(pids[childNo], NULL, WNOHANG) < 0)
			{
				readChannel(channelRead, &pid);
			}
		}
		closeChannel(channelRead);
	}
}

int main(int argc, char** argv)
{
	int retCode = -1;
	if (argc == 2)
	{
		// -- expecting an int in argv[1]
		int noOfChildren;
		if (sscanf(argv[1], "%d", &noOfChildren) != 1)
		{
			printf("ERROR: # of channels is invalid, aborting\n");
		}
		else
		{
			// -- create channel 
			int create = createChannel("gugu01", sizeof(pid_t));
			if (create < 0)
			{
				perror("channelCreate failed");
			}
			else
			{
				printf("channelCreate returned %d\n", create);
				
				// -- run parent
				runParent(noOfChildren);
			
				// -- delete the channel
				deleteChannel("gugu01");
				
			}
		}
	}
	else
	{
		usage(argv[0]);
	}
	return retCode;
}
